#pragma once
#include <QtGui/QWindow>
#include <QtGui/QOpenGLFunctions>
#include <QtWidgets/QOpenGLWidget>
#include <QOpenGLVertexArrayObject>
#include <QPainter>
#include <QOpenGLContext>
#include <QOpenGLPaintDevice>
#include <QOpenGLShaderProgram>
#include <QOpenGLShader>
#include <QTimer>
#include <QVector3D>
#include <QQuaternion>
#include <QMouseEvent>
#include <QKeyEvent>
#include <QTime>
#include <QScopedPointer>
#include "minimumboundbox.h"

using namespace std;

QT_BEGIN_NAMESPACE
class QPainter;
class QOpenGLContext;
class QOpenGLPaintDevice;
class QOpenGLShaderProgram;
class QOpenGLShader;
QT_END_NAMESPACE

class OpenglWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
    Q_OBJECT
public:
    explicit OpenglWidget(QWidget *parent = 0);
    ~OpenglWidget();
    /// @brief 显示点云
    void showPointCloud(const std::vector<QVector3D> &cloud);
    /// @brief 当窗口大小改变时调用，设置视口大小
    virtual void resizeGL(int w, int h);

protected:
    /// @brief 初始化 OpenGL，包括着色器、缓冲区、顶点数组对象
    virtual void initializeGL(void);
    virtual void paintGL(void);

    struct VertexInfo
    {
        float pos[3] = {0.0, 0.0, 0.0};
        float normal[3] = {0.0, 0.0, 1.0};
        float color[4] = {1.0, 1.0, 1.0, 1.0};
    };
private slots:
    /// @brief 定时器超时刷新函数
    void onTimerOut(void);
    virtual void mousePressEvent(QMouseEvent *e);
    virtual void mouseMoveEvent(QMouseEvent *e);
    virtual void mouseReleaseEvent(QMouseEvent *e);
    virtual void wheelEvent(QWheelEvent *e);
    virtual void keyPressEvent(QKeyEvent *e);
    virtual void leaveEvent(QEvent *);
    virtual void enterEvent(QEvent *);

private:
    void setBackgroundColor(QVector3D color);
    /// @brief 获取着色器中的uniform变量位置
    void GetShaderUniformPara();
    /// @brief 初始化OpenGL着色器
    /// @return
    bool InitShader();
    /// @brief 根据鼠标移动进行线性移动
    /// @param posOrgin
    /// @param posEnd
    void LineMove(QVector2D posOrgin, QVector2D posEnd);
    /// @brief 根据鼠标移动进行旋转
    /// @param posOrgin
    /// @param posEnd
    void Rotate(QVector2D posOrgin, QVector2D posEnd);
    /// @brief 根据鼠标移动进行缩放
    void modelZoomInOrOut(bool ZoomInOrOut);
    /// @brief 将像素位置转换为视图位置
    /// @param p
    /// @return
    QVector3D pixelPosToViewPos(const QVector2D &p);
    /// @brief 计算旋转
    /// @param posOrgin
    /// @param posEnd
    void calRotation(QVector2D posOrgin, QVector2D posEnd);
    /// @brief 初始化点云数据
    void initPointCloud(const std::vector<QVector3D> &cloud);
    /// @brief 根据点的深度（Z坐标）进行伪彩色编码
    /// @param pos
    /// @param color
    void gray2Pseudocolor(const QVector3D pos, float color[4]);
    /// @brief 更新点云数据到 GPU
    void changePointCloud();
    void setMatrixUniform();
    /// @brief 重置视图，包括旋转、缩放和移动
    void ResetView();
    /// @brief 初始化点云，添加一个默认点
    void initCloud();
    /// @brief 添加坐标轴数据
    void addAxisData();

private:
    /// @brief OpenGL
    QTimer *m_Timer;
    QScopedPointer<QOpenGLShaderProgram> m_Program; // 着色器程序指针，用于存储和链接顶点和片段着色器
    QScopedPointer<QOpenGLVertexArrayObject> m_VAO; // 顶点数组对象指针，用于存储顶点属性的配置
    /// @brief 着色器
    GLuint m_posAttr;       // 顶点属性位置
    GLuint m_colAttr;       // 顶点属性颜色
    GLuint m_norAttr;       // 顶点属性法线
    GLuint m_matrixUniform; // uniform变量位置
    GLuint m_VBO;           // 顶点缓冲对象，用于存储顶点数据
    GLuint m_EBO;           // 元素缓冲对象，用于存储索引数据
    /// @brief 点云和坐标轴数据
    QVector<VertexInfo> m_PointsVertex; // 存储点云顶点信息的向量
    MinimumBoundBox m_box;              // 最小边界框，用于计算点云的边界
    GLuint m_Axisindices[6];            // 坐标轴的索引数组
    /// @brief 视图变换
    QVector3D m_lineMove;              // 线性移动向量
    QQuaternion m_rotate;              // 旋转四元数
    float m_scale;                     // 缩放因子
    bool m_bShowAxis;                  // 是否显示坐标轴
    QVector2D m_rightButtonInitialPos; // 右键按下的初始位置
    QVector2D m_lastPoint;             // 上一次鼠标的位置
    QVector4D m_backgroundColor;       // 背景颜色
};
